package com.chuanke.ckfamily.service.common.impl;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.ibatis.cache.Cache;
import org.apache.log4j.Logger;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 
 * ClassName: MybatisRedisCache <br/>
 * 实现Mybatis Cache接口，定义二级缓存, 抛弃SqlSession 的一级缓存<br/>
 * date: 2016年11月6日 下午12:53:25 <br/>
 * @author yc
 * @version 1.0
 * @since JDK 1.7
 */
public class MybatisRedisCache implements Cache  {
	private static final Logger logger = Logger.getLogger(MybatisRedisCache.class);

    private static JedisConnectionFactory jedisConnectionFactory;

    private final String id;
    private final String COMMON_CACHE_KEY = "COM:";

    /**
     * The {@code ReadWriteLock}.
     */
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
    /**
     * 按照一定规则标识key
     */
    private String getKey(Object key) {
        StringBuilder accum = new StringBuilder();
        accum.append(COMMON_CACHE_KEY);
        accum.append(this.id).append(":");
        accum.append(DigestUtils.md5Hex(String.valueOf(key)));
        return accum.toString();
    }


    public MybatisRedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        logger.debug("MybatisRedisCache:id=" + id);
        this.id = id;
    }

    @Override
    public void clear()
    {
    	RedisConnection  connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            connection.flushDb();
            connection.flushAll();
        }
        catch (JedisConnectionException e)
        {
            logger.error("MYBATIS REDIS clear ERROR ", e);
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
    }

    @Override
    public String getId()
    {
        return this.id;
    }

    @Override
    public Object getObject(Object key)
    {
        Object result = null;
        RedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            String keyString = getKey(key);
            byte[] serKey = SerializeUtil.serialize(keyString);
            result = SerializeUtil.unserialize(connection.get(serKey));
        }
        catch (JedisConnectionException e)
        {
        	   logger.error("MYBATIS REDIS getObject ERROR ", e);
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    @Override
    public ReadWriteLock getReadWriteLock()
    {
        return this.readWriteLock;
    }

    @Override
    public int getSize()
    {
        int result = 0;
        RedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            result = Integer.valueOf(connection.dbSize().toString());
        }
        catch (JedisConnectionException e)
        {
            logger.error("MYBATIS REDIS getSize ERROR ", e);
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    @Override
    public void putObject(Object key, Object value)
    {
    	RedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            String keyString = getKey(key);
            connection.set(SerializeUtil.serialize(keyString), SerializeUtil.serialize(value));
        }
        catch (JedisConnectionException e)
        {
            logger.error("MYBATIS REDIS putObject ERROR ", e);
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
    }

    @Override
    public Object removeObject(Object key)
    {
    	RedisConnection connection = null;
        Object result = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            String keyString = getKey(key);
            result =connection.expire(SerializeUtil.serialize(keyString), 0);
        }
        catch (JedisConnectionException e)
        {
            logger.error("MYBATIS REDIS removeObject ERROR ", e);
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        MybatisRedisCache.jedisConnectionFactory = jedisConnectionFactory;
    }
	 
   static class SerializeUtil {
    	public static byte[] serialize(Object object) {
    		ObjectOutputStream oos = null;
    		ByteArrayOutputStream baos = null;
    		try {
    			// 序列化
    			baos = new ByteArrayOutputStream();
    			oos = new ObjectOutputStream(baos);
    			oos.writeObject(object);
    			byte[] bytes = baos.toByteArray();
    			return bytes;
    		} catch (Exception e) {
    			logger.error("MYBATIS REDIS serialize ERROR ", e);
    		}
    		return null;
    	}

    	public static Object unserialize(byte[] bytes) {
    		if(bytes == null)return null;
    		ByteArrayInputStream bais = null;
    		try {
    			// 反序列化
    			bais = new ByteArrayInputStream(bytes);
    			ObjectInputStream ois = new ObjectInputStream(bais);
    			return ois.readObject();
    		} catch (Exception e) {
    			logger.error("MYBATIS REDIS unserialize ERROR ", e);
    		}
    		return null;
    	}
    }
}
